﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;

namespace PDX
{
    /// <summary>
    /// The purpose of this interface is to provide an easy way for
    /// clients to subscribe to changes in PDX-related DependencyProperties.
    /// </summary>
    /// <remarks>
    /// DependencyPropertyDescriptor.FromProperty(...).AddValueChanged(...) is
    /// problematic because some long-lived .NET internal creates a hard
    /// reference to you, meaning you have to unsubscribe (or use some type of weak event).
    /// Knowing when to unsubscribe is nearly impossible if you want your lifecycle
    /// to match that of a FrameworkElement that gets Loaded and Unloaded repeatedly.
    /// It would be OK if you could just unsubscribe when you're Unloaded and
    /// re-subscribe when you're Loaded, but sometimes you want to do work just
    /// before you're Loaded. If you wait until you're Loaded, you can get some
    /// unpleasant screen flickering. With this event, you can stay subscribed
    /// all the time and your lifecycle can be as short as the element you're
    /// subscribed to.
    /// 
    /// Another approach would be to define an attached event for each property
    /// and raise it, but that's extra code and I think UIElement.AddHandler(...)
    /// was creating hard references anyway, making it just as bad.
    /// </remarks>
    public interface IRaiseDependencyPropertyChanges
    {
        /// <summary>
        /// Occurs when the value of a PDX-related DependencyProperty changes.
        /// </summary>
        event DependencyPropertyChangedEventHandler DependencyPropertyChanged;

        /// <summary>
        /// Raises the <see cref="DependencyPropertyChanged"/> event with the
        /// specified <see cref="System.Windows.DependencyPropertyChangedEventArgs"/>.
        /// </summary>
        /// <param name="args">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        void Raise(DependencyPropertyChangedEventArgs args);

        /// <summary>
        /// This interface is only meant to be implemented by FrameworkElements.
        /// Exposes this object as a FrameworkElement
        /// </summary>
        FrameworkElement Self { get; }
    }

    /// <summary>
    /// Helpful for setting up PropertyChangedCallbacks by elements that
    /// implement <see cref="IRaiseDependencyPropertyChanges"/>
    /// </summary>
    public static class Raiser
    {
        /// <summary>
        /// A useful method to set as a PropertyChangedCallback for a
        /// DependencyProperty if you implement <see cref="IRaiseDependencyPropertyChanges"/>.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="args">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        public static void Raise(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            var raiser = sender as IRaiseDependencyPropertyChanges;
            if (raiser != null)
            {
                raiser.Raise(args);
            }
        }
    }
}
